﻿using System;

namespace Aeon.Emulator.Sound.FM.Operators
{
    /// <summary>
    /// Emulates a single OPL operator.
    /// </summary>
    internal class StandardOperator : OperatorBase
    {        
        private static readonly Random random = new Random();

        protected readonly FmSynthesizer opl;

        /// <summary>
        /// Initializes a new instance of the Operator class.
        /// </summary>
        /// <param name="baseAddress">Base operator register address.</param>
        /// <param name="opl">FmSynthesizer instance which owns the operator.</param>
        public StandardOperator(int baseAddress, FmSynthesizer opl)
            : base(baseAddress, opl)
        {
            this.opl = opl;
        }

        /// <summary>
        /// Returns the current output value of the operator.
        /// </summary>
        /// <param name="modulator">Modulation factor to apply to the output.</param>
        /// <returns>Current output value of the operator.</returns>
        public virtual double GetOperatorOutput(double modulator)
        {
            if(this.AdsrState == AdsrState.Off)
                return 0;

            double envelopeInDB = this.GetEnvelope();
            this.Envelope = Math.Pow(10, envelopeInDB / 10.0);

            this.RecalculatePhase();

            return this.GetOutput(modulator, this.Phase, this.SelectedWaveform);
        }
        public double GetOperatorOutput()
        {
            return this.GetOperatorOutput(0);
        }

        /// <summary>
        /// Returns a random value between 0.0 and 1.0.
        /// </summary>
        /// <returns>Random value between 0.0 and 1.0.</returns>
        protected static double GetRandomDouble()
        {
            lock(random)
            {
                return random.NextDouble();
            }
        }

        protected double GetOutput(double modulator, double outputPhase, OperatorWaveform waveform)
        {
            outputPhase = (outputPhase + modulator) % 1;
            if(outputPhase < 0)
            {
                outputPhase++;
                outputPhase %= 1;
            }

            //int sampleIndex = (int)(outputPhase * waveform.Length);
            //return waveform[sampleIndex] * this.Envelope;
            return waveform.GetValue(outputPhase) * this.Envelope;
        }
    }
}
